package com.hedl.practice.diyJava.encryptionandsecurity.hmac_algorithm.test;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.kerberos.EncryptionKey;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
        /**
         * Hmac算法就是一种基于密钥的消息认证码算法，它的全称是Hash-based Message Authentication Code，是一种更安全的消息摘要算法
         * Hmac算法总是和某种哈希算法配合起来用的。例如，我们使用MD5算法，对应的就是HmacMD5算法，它相当于“加盐”的MD5：
         *
         * HmacMD5可以看作带有一个安全的key的MD5。使用HmacMD5而不是用MD5加salt，有如下好处：
         *      HmacMD5使用的key长度是64字节，更安全；
         *      Hmac是标准算法，同样适用于SHA-1等其他哈希算法；
         *      Hmac输出和原有的哈希算法长度一致。
         *
         * Hmac本质上就是把key混入摘要的算法。验证此哈希时，除了原始的输入数据，还要提供key。
         * 为了保证安全，我们不会自己指定key，而是通过Java标准库的KeyGenerator生成一个安全的随机的key
         */

        //获取 HmacMD5 算法的 KeyGenerator 实例
        KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");
        // 生成一个随机的 SecretKey
        SecretKey key = keyGen.generateKey();
        // 打印随机生成的key:
        byte[] encoded = key.getEncoded();
        System.out.println(new BigInteger(1, encoded).toString(16));

        // 获取 HmacMD5 算法的 Mac 实例
        Mac mac = Mac.getInstance("HmacMD5");
        // 使用生成的 key 初始化 Mac
        mac.init(key);
        // 更新 Mac 的数据为 "HelloWorld"
        mac.update("HelloWorld".getBytes(StandardCharsets.UTF_8));
        // 计算 HMAC-MD5 认证码
        byte[] bytes = mac.doFinal();
        // 打印计算结果
        System.out.println(new BigInteger(1, bytes).toString(16));

        System.out.println("================================================");

        /**
         * 和MD5相比，使用HmacMD5的步骤是：
         *      通过名称HmacMD5获取KeyGenerator实例；
         *      通过KeyGenerator创建一个SecretKey实例；
         *      通过名称HmacMD5获取Mac实例；
         *      用SecretKey初始化Mac实例；
         *      对Mac实例反复调用update(byte[])输入数据；
         *      调用Mac实例的doFinal()获取最终的哈希值。
         */

        //有了Hmac计算的哈希和SecretKey，我们想要验证怎么办？这时，SecretKey不能从KeyGenerator生成，而是从一个byte[]数组恢复：
        byte[] hkey = new byte[] { 106, 70, -110, 125, 39, -20, 52, 56, 85, 9, -19, -72, 52, -53, 52, -45, -6, 119, -63,
                30, 20, -83, -28, 77, 98, 109, -32, -76, 121, -106, 0, -74, -107, -114, -45, 104, -104, -8, 2, 121, 6,
                97, -18, -13, -63, -30, -125, -103, -80, -46, 113, -14, 68, 32, -46, 101, -116, -104, -81, -108, 122,
                89, -106, -109 };
        SecretKey key1 = new SecretKeySpec(hkey,"HmacMD5");     //恢复SecretKey的语句
        Mac mac1 = Mac.getInstance("HmacMd5");
        mac1.init(key1);        //使用给定键初始化此 Mac对象。
        mac1.update("HelloWorld".getBytes(StandardCharsets.UTF_8));
        byte[] bytes1 = mac1.doFinal();
        System.out.println(Arrays.toString(bytes1));        //[126, 59, 37, 63, 73, 90, 111, -96, -77, 15, 82, -74, 122, -55, -67, 54]


        //小结：
        //      Hmac算法是一种标准的基于密钥的哈希算法，可以配合MD5、SHA-1等哈希算法，计算的摘要长度和原摘要算法长度相同
    }
}
